package br.ufc.great.dtx.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import br.ufc.great.client.DTXResult;
import br.ufc.great.client.DTXTransaction;
import br.ufc.great.dtx.tm.Transaction;
import br.ufc.great.dtx.tm.TransactionManager;
import br.ufc.great.dtx.tm.deadlockdetector.RemoteWFGMessage;
import br.ufc.great.dtx.tm.deadlockdetector.WFGMessage;
import br.ufc.great.dtx.tm.operations.Operation;
import br.ufc.great.dtx.tm.sch.Message;
import br.ufc.great.dtx.tm.sch.RemoteMessage;
import br.ufc.great.dtx.tm.sch.RemoteOperation;
import br.ufc.great.dtx.tm.sch.Scheduler;

/**
 * 
 * @author Leonardo Oliveira Moreira
 *
 * Classe que implementa o servidor auxiliar
 */
public class DTXServerThread extends Thread {

	private Socket request;
	private TransactionManager transactionManager = DTXStart.getTransactionManager();
	private DTXOutput output;
	
	public DTXServerThread(DTXOutput output, Socket request) {
		setPriority(MIN_PRIORITY);
		this.output = output;
		this.request = request;
	}
	
	/**
	 * Método de execu��o do servidor auxiliar
	 */
	public void run() {
		try {
			ObjectInputStream inputStream = new ObjectInputStream(request.getInputStream());
			Object object = inputStream.readObject();
			if (object instanceof DTXTransaction) {
				Transaction transaction = Transaction.toTransaction((DTXTransaction) object);
				transactionManager.execute(transaction, this);	
				synchronized (this) {
					wait();				
				}
				output.print(getClass().getSimpleName(), "Generating results for transaction " + transaction.getId(), DTXOutput.TEST);
				DTXResult dtxResult = Transaction.transactionToDTXResult(transaction);
				output.print(getClass().getSimpleName(), "Seding results of transaction " + transaction.getId() + " to client", DTXOutput.TEST);
				ObjectOutputStream outputStream = new ObjectOutputStream(request.getOutputStream());
				outputStream.writeObject(dtxResult);
				output.print(getClass().getSimpleName(), "Results os transaction " + transaction.getId() + " sent to client", DTXOutput.TEST);
				outputStream.flush();
				outputStream.close();
				inputStream.close();
				request.close();
			}
			else {
				Scheduler scheduler = transactionManager.getScheduler();
				if (object instanceof Operation) {
					output.print(getClass().getSimpleName(), "Remote operation arrived for execute in localhost", DTXOutput.TEST);
					RemoteOperation remoteOperation = new RemoteOperation(request, (Operation) object);
					scheduler.sendRemoteOperation(remoteOperation);
				}
				else {
					if (object instanceof Message) {
						output.print(getClass().getSimpleName(), "Remote message arrived for execute in localhost", DTXOutput.TEST);
						RemoteMessage remoteMessage = new RemoteMessage(request, (Message) object);
						scheduler.sendRemoteMessage(remoteMessage);
					}
					else {
						//output.print(getClass().getSimpleName(), "Remote WFG message arrived for execute in localhost", DTXOutput.TEST);
						RemoteWFGMessage remoteWFGMessage = new RemoteWFGMessage(request, (WFGMessage) object);
						scheduler.sendRemoteWFGMessage(remoteWFGMessage);
					}
				}
			}
		}
		catch (IOException e) {
			output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.ERROR);
		}
		catch (ClassNotFoundException e) {
			output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.ERROR);
		}
		catch (InterruptedException e) {
			output.print(getClass().getSimpleName(), e.getMessage(), DTXOutput.ERROR);
		}
	}
	
	/**
	 * Método utilizado para encerrar o servidor auxiliar
	 */
	public synchronized void end() {
		notify();
	}
	
}
